package com.ctshk.rpc.scenic.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctshk.common.dto.Result;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.model.PageResponse;
import com.ctshk.common.utils.DateUtil;
import com.ctshk.common.utils.EntityUtil;
import com.ctshk.rpc.scenic.dto.TicketLuckSaveDTO;
import com.ctshk.rpc.scenic.dto.TicketTypeLuckDrawDTO;
import com.ctshk.rpc.scenic.dto.TicketTypeLuckDrawNumDTO;
import com.ctshk.rpc.scenic.dto.TicketTypeLuckDrawUserDTO;
import com.ctshk.rpc.scenic.entity.TicketLuckDrawApplyUserInfo;
import com.ctshk.rpc.scenic.entity.TicketTypeLuckDraw;
import com.ctshk.rpc.scenic.entity.TicketTypeLuckDrawUser;
import com.ctshk.rpc.scenic.listenner.UploadDataListener;
import com.ctshk.rpc.scenic.mapper.TicketLuckDrawApplyUserInfoMapper;
import com.ctshk.rpc.scenic.mapper.TicketTypeLuckDrawMapper;
import com.ctshk.rpc.scenic.mapper.TicketTypeLuckDrawUserMapper;
import com.ctshk.rpc.scenic.req.CheckDuplicateReq;
import com.ctshk.rpc.scenic.req.TicketLuckDrawApplyUserInfoReq;
import com.ctshk.rpc.scenic.req.TicketTypeLuckDrawExcel;
import com.ctshk.rpc.scenic.req.TicketTypeLuckDrawReq;
import com.ctshk.rpc.scenic.service.ITicketTypeLuckDrawService;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 门票抽奖种类表 服务实现类
 * </p>
 *
 * @author 谢诗宏
 * @since 2021-02-18
 */
@DubboService
public class TicketTypeLuckDrawServiceImpl extends ServiceImpl<TicketTypeLuckDrawMapper, TicketTypeLuckDraw> implements ITicketTypeLuckDrawService {


    @Autowired
    private TicketTypeLuckDrawMapper ticketTypeLuckDrawMapper;

    @Autowired
    private TicketTypeLuckDrawUserMapper typeLuckDrawUserMapper;

    @Autowired
    private TicketLuckDrawApplyUserInfoMapper ticketLuckDrawApplyUserInfoMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 购票数量上限, 类型上限
      */
    private static final Integer TICKET_NUM_MAX = 4;
    private static final Integer TICKET_TYPE_NUM_MAX = 4;
    @Override
    public void upload(byte[] pic) {
        InputStream inputStream = new ByteArrayInputStream(pic);
        try {
            EasyExcel.read(inputStream, TicketTypeLuckDrawExcel.class, new UploadDataListener(this)).sheet().doRead();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public PageResponse<TicketTypeLuckDrawDTO> listAll() {
        IPage<TicketTypeLuckDraw> iPage = new Page<>(1, 5);
        IPage<TicketTypeLuckDraw> registrationIPage = baseMapper.selectPage(iPage, null);
        if (CollectionUtils.isNotEmpty(registrationIPage.getRecords())) {
            List<TicketTypeLuckDrawDTO> scenicResourceTicketDTOS = EntityUtil.copyList(registrationIPage.getRecords(), TicketTypeLuckDrawDTO.class);
            return new PageResponse<>(scenicResourceTicketDTOS, iPage.getCurrent() == 1, iPage.getTotal() <= (iPage.getCurrent() * iPage.getSize()), iPage.getTotal(), iPage.getCurrent(), iPage.getSize());
        }
        return new PageResponse<>(new ArrayList<>(), iPage.getCurrent() == 1, iPage.getTotal() <= (iPage.getCurrent() * iPage.getSize()), iPage.getTotal(), iPage.getCurrent(), iPage.getSize());

    }

    @Override
    public Result<PageResponse<TicketTypeLuckDrawDTO>> queryCondition(TicketTypeLuckDrawReq ticketReq) {
        IPage<TicketTypeLuckDraw> iPage = new Page<>(ticketReq.getPageNo(), ticketReq.getPageSize());
        QueryWrapper<TicketTypeLuckDraw> queryWrapper = new QueryWrapper<>();
        if (null != ticketReq.getIds()) {
            queryWrapper.lambda().in(ticketReq.getIds().size() > 0, TicketTypeLuckDraw::getId, ticketReq.getIds());
        }
        if (StringUtils.isNotEmpty(ticketReq.getSessionsName())) {
            queryWrapper.lambda().like(TicketTypeLuckDraw::getSessionsName, ticketReq.getSessionsName());
        }
        if (StringUtils.isNotEmpty(ticketReq.getDiscipline())) {
            queryWrapper.lambda().like(TicketTypeLuckDraw::getDiscipline, ticketReq.getDiscipline());
        }
        if (StringUtils.isNotEmpty(ticketReq.getCompetitionArea())) {
            queryWrapper.lambda().like(TicketTypeLuckDraw::getCompetitionArea, ticketReq.getCompetitionArea());
        }
        if (StringUtils.isNotEmpty(ticketReq.getFareBasis())) {
            queryWrapper.lambda().like(TicketTypeLuckDraw::getFareBasis, ticketReq.getFareBasis());
        }
        if (null != ticketReq.getCompetitionStartDate() && null != ticketReq.getCompetitionEndDate()) {
            queryWrapper.lambda().ge(TicketTypeLuckDraw::getCompetitionDate, ticketReq.getCompetitionStartDate());
            queryWrapper.lambda().lt(TicketTypeLuckDraw::getCompetitionDate, ticketReq.getCompetitionEndDate().plusDays(1));
        }

        queryWrapper.lambda().in(CollectionUtils.isNotEmpty(ticketReq.getCompetitionAreaList()), TicketTypeLuckDraw::getCompetitionArea, ticketReq.getCompetitionAreaList());
        queryWrapper.lambda().in(CollectionUtils.isNotEmpty(ticketReq.getFareBasisList()), TicketTypeLuckDraw::getFareBasis, ticketReq.getFareBasisList());

        IPage<TicketTypeLuckDraw> registrationIPage = baseMapper.selectPage(iPage, queryWrapper);
        if (CollectionUtils.isNotEmpty(registrationIPage.getRecords())) {
            List<TicketTypeLuckDrawDTO> registrationDTOS = EntityUtil.copyList(registrationIPage.getRecords(), TicketTypeLuckDrawDTO.class);
            Map<Long, TicketTypeLuckDraw> maps = registrationIPage.getRecords().stream().collect(Collectors.toMap(TicketTypeLuckDraw::getId, Function.identity()));
            registrationDTOS.forEach(registration -> {
                registration.setCompetitionDate(maps.get(registration.getId()).getCompetitionDate().toLocalDate());
            });
            //根据uuid获取上次已加减的票数
            if (StringUtils.isNotBlank(ticketReq.getUuid())) {
                Object o = redisTemplate.opsForValue().get("ticketTypeLuckDrawCache" + ticketReq.getUuid());
                if (null != o) {
                    Map<String, Integer> map = (Map<String, Integer>) o;
                    List<String> ids = new ArrayList<>(map.keySet());
                    registrationDTOS.forEach(dto -> {
                        ids.forEach(id -> {
                            if (dto.getId().toString().equals(id)) {
                                dto.setCacheNumber(map.get(id));
                            }
                        });
                    });
                }
            }
            return Result.success(new PageResponse<>(registrationDTOS, iPage.getCurrent() == 1, iPage.getTotal() <= (iPage.getCurrent() * iPage.getSize()), iPage.getTotal(), iPage.getCurrent(), iPage.getSize()));
        }
        return Result.success(new PageResponse<>(new ArrayList<>(), iPage.getCurrent() == 1, iPage.getTotal() <= (iPage.getCurrent() * iPage.getSize()), iPage.getTotal(), iPage.getCurrent(), iPage.getSize()));


    }

    /**
     * 点击立即报名->保存三张表
     * 以及查询出queryNumber 和 门票类型和其数量（根据queryNumber）
     *
     * @param
     * @param userInfoReq
     * @return
     */
    @Override
    @Transactional
    public Result saveTicket(TicketLuckDrawApplyUserInfoReq userInfoReq) {
        // 校验是否重复报名
        if (StringUtils.isEmpty(userInfoReq.getPhone())|| !isHkPhoneLegal(userInfoReq.getPhone())) {
            return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20029));
        }
        if (StringUtils.isEmpty(userInfoReq.getIdNumber())|| !isIdNumber(userInfoReq.getIdNumber())) {
            return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20028));
        }
        if (StringUtils.isEmpty(userInfoReq.getMail())|| !isEmail(userInfoReq.getMail())) {
            return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20030));
        }
        Integer count = ticketLuckDrawApplyUserInfoMapper.selectCount(new QueryWrapper<TicketLuckDrawApplyUserInfo>().lambda()
                .eq(TicketLuckDrawApplyUserInfo::getIdNumber, userInfoReq.getIdNumber()).or()
                .eq(TicketLuckDrawApplyUserInfo::getPhone, userInfoReq.getPhone()));
        if (count != 0) {
            return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20027));
        }

        //用户表
        //查询编号规则：   TK+時間戳+3隨機數
        String queryNumber = "TK" + System.currentTimeMillis() + (int) (Math.random() * 900) + 100;
        System.out.println("查詢編號: " + queryNumber);
        userInfoReq.setQueryNumber(queryNumber);
        TicketLuckDrawApplyUserInfo ticketLuckDrawApplyUserInfo = EntityUtil.convertBean(userInfoReq, TicketLuckDrawApplyUserInfo.class);
        ticketLuckDrawApplyUserInfoMapper.insert(ticketLuckDrawApplyUserInfo);
        for (TicketTypeLuckDrawReq ticketTypeLuckDrawReq : userInfoReq.getTickets()) {
            if (ticketTypeLuckDrawReq.getNumber() > TICKET_NUM_MAX) {
                return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20026));
            }
            //中间表
            TicketTypeLuckDrawUser drawUser = new TicketTypeLuckDrawUser();
            drawUser.setNumber(ticketTypeLuckDrawReq.getNumber());
            drawUser.setTicketTypeId(ticketTypeLuckDrawReq.getId());
            drawUser.setUserInfoId(ticketLuckDrawApplyUserInfo.getId());
            drawUser.setWinning(0);
            typeLuckDrawUserMapper.insert(drawUser);
        }
        List<TicketTypeLuckDrawNumDTO> ticketTypeAndNumList = this.selectTicketByQueryNumber(queryNumber);
        TicketLuckSaveDTO ticketLuckSaveDTO = new TicketLuckSaveDTO();
        ticketLuckSaveDTO.setQueryNumber(queryNumber);
        ticketLuckSaveDTO.setTicketTypeAndNumList(ticketTypeAndNumList);
        return Result.success(ticketLuckSaveDTO);
    }

    /**
     * 校验身份证,联系电话,邮箱等信息
     * @param userInfoReq
     */
    private void checkUserInfoReq(TicketLuckDrawApplyUserInfoReq userInfoReq) {
        if (StringUtils.isEmpty(userInfoReq.getPhone())|| !isHkPhoneLegal(userInfoReq.getPhone())) {
            throw new BusinessException(SystemError.SEASON_TICKET_20029);
        }
        if (StringUtils.isEmpty(userInfoReq.getIdNumber())|| !isIdNumber(userInfoReq.getIdNumber())) {
            throw new BusinessException(SystemError.SEASON_TICKET_20028);
        }
        if (StringUtils.isEmpty(userInfoReq.getMail())|| !isEmail(userInfoReq.getMail())) {
            throw new BusinessException(SystemError.SEASON_TICKET_20030);
        }
    }

    /**
     * 香港手机号码8位数，1~9开头+7位任意数
     */
    private boolean isHkPhoneLegal(String str) {
        String regExp = "^([1-9])\\d{7}$";
        Pattern p = Pattern.compile(regExp);
        Matcher m = p.matcher(str);
        return m.matches();
    }

    /**
     * 香港身份证校验
     */
    private boolean isIdNumber(String str) {
        String regExp = "^((\\s?[A-Za-z])|([A-Za-z]{2}))\\d{6}((\\([0-9A-Za-z]\\))|([0-9A-Za-z]))$";
        Pattern p = Pattern.compile(regExp);
        Matcher m = p.matcher(str);
        return m.matches();
    }

    /**
     * 邮箱校验
     */
    private boolean isEmail(String str) {
        String check = "^([a-z0-9A-Z]+[-|.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
        Pattern regex = Pattern.compile(check);
        Matcher matcher = regex.matcher(str);
        return matcher.matches();
    }

    /**
     * 保存excel数据到DB
     *
     * @param
     */
    @Override
    public void saveTicket(List<TicketTypeLuckDrawExcel> ticketTypeList) {
        List<TicketTypeLuckDraw> drawList = new ArrayList<>();
        for (TicketTypeLuckDrawExcel ticketTypeLuckDrawExcel : ticketTypeList) {

            TicketTypeLuckDraw typeLuckDraw = new TicketTypeLuckDraw();
            BeanUtils.copyProperties(ticketTypeLuckDrawExcel, typeLuckDraw);
            LocalDateTime parse = DateUtil.parse(ticketTypeLuckDrawExcel.getCompetitionDate());
            typeLuckDraw.setCompetitionDate(parse);
            typeLuckDraw.setPrice(new BigDecimal(ticketTypeLuckDrawExcel.getPrice()));
            drawList.add(typeLuckDraw);
        }
        System.out.println("saveBatch（）次数");
        this.saveBatch(drawList);
    }

    @Override
    public List<TicketTypeLuckDrawNumDTO> selectTicketByQueryNumber(String queryNumber) {
        //查询奥运会 门票类型和数量
        return baseMapper.selectByQueryNumber(queryNumber);
    }

    @Override
    public List<TicketTypeLuckDrawDTO> selectTicketTypeLuckDrawList() {
        List<TicketTypeLuckDraw> ticketList = baseMapper.selectList(null);
        List<TicketTypeLuckDrawDTO> scenicResourceTicketDTOS = EntityUtil.copyList(ticketList, TicketTypeLuckDrawDTO.class);
        return scenicResourceTicketDTOS;
    }

    @Override
    public List<TicketTypeLuckDrawDTO> queryTicketTypeLuckDrawByIds(List<String> ids) {
        QueryWrapper<TicketTypeLuckDraw> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(null != ids && ids.size() > 0, TicketTypeLuckDraw::getId, ids);
        List<TicketTypeLuckDraw> ticketTypeLuckDraws = ticketTypeLuckDrawMapper.selectList(queryWrapper);
        Map<Long, TicketTypeLuckDraw> map = ticketTypeLuckDraws.stream().collect(Collectors.toMap(TicketTypeLuckDraw::getId, Function.identity()));
        List<TicketTypeLuckDrawDTO> registrationDTOS = EntityUtil.copyList(ticketTypeLuckDraws, TicketTypeLuckDrawDTO.class);
        registrationDTOS.forEach(ticketTypeLuckDraw -> {
            ticketTypeLuckDraw.setCompetitionDate(map.get(ticketTypeLuckDraw.getId()).getCompetitionDate().toLocalDate());
        });
        return registrationDTOS;
    }

    @Override
    public Result checkDuplicate(CheckDuplicateReq checkDuplicateReq) {
        switch (checkDuplicateReq.getType()) {
            case "1":
                if (StringUtils.isBlank(checkDuplicateReq.getIdNumber())) {
                    return Result.failed(new BusinessException(SystemError.SYS_409, "idNumber"));
                }
                Integer idNumberCount = ticketLuckDrawApplyUserInfoMapper.selectCount(new QueryWrapper<TicketLuckDrawApplyUserInfo>().lambda()
                        .eq(TicketLuckDrawApplyUserInfo::getIdNumber, checkDuplicateReq.getIdNumber()));
                if (idNumberCount != 0) {
                    return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20033));
                }
                return Result.success();
            case "2":
                if (StringUtils.isBlank(checkDuplicateReq.getPhone())) {
                    return Result.failed(new BusinessException(SystemError.SYS_409, "phone"));
                }
                Integer phoneCount = ticketLuckDrawApplyUserInfoMapper.selectCount(new QueryWrapper<TicketLuckDrawApplyUserInfo>().lambda()
                        .eq(TicketLuckDrawApplyUserInfo::getPhone, checkDuplicateReq.getPhone()));
                if (phoneCount != 0) {
                    return Result.failed(new BusinessException(SystemError.SEASON_TICKET_20034));
                }
                return Result.success();
            default:
        }
        return Result.success();
    }

    @Override
    public Result checkCacheSession(String uuid) {
        String key = "ticketTypeLuckDrawCache" + uuid;
        Object sessionMap = redisTemplate.opsForValue().get(key);
        if (!Objects.isNull(sessionMap)) {
            Map<String, Integer> map = (Map<String, Integer>) sessionMap;
            if (map.keySet().size() > TICKET_TYPE_NUM_MAX) {
                return Result.failed(SystemError.SEASON_TICKET_20035);
            }
            for (Integer cacheNum : map.values()) {
                if (cacheNum > TICKET_NUM_MAX) {
                    return Result.failed(SystemError.SEASON_TICKET_20035);
                }
            }
        }
        return Result.success();
    }


    /**
     * 抽签
     *
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result drawTicket(Long loginId) {
        //1.获取所有未抽中的报名信息
        List<TicketTypeLuckDrawUserDTO> luckDrawUsers = typeLuckDrawUserMapper.selectDrawUserList();
        if (org.springframework.util.CollectionUtils.isEmpty(luckDrawUsers)) {
            return Result.success(SystemError.SEASON_TICKET_20038.getMsg());
        }

        // 获取所有待抽奖票列表
        List<Long> ticketIdList = luckDrawUsers.stream().map(TicketTypeLuckDrawUserDTO::getTicketTypeId).distinct().collect(Collectors.toList());
        List<TicketTypeLuckDraw> ticketTypeLuckDrawList = ticketTypeLuckDrawMapper.selectList(
                new QueryWrapper<TicketTypeLuckDraw>().lambda().in(TicketTypeLuckDraw::getId, ticketIdList));
        if (org.springframework.util.CollectionUtils.isEmpty(ticketTypeLuckDrawList)) {
            return Result.success();
        }

        // 唯一keyMap
        Map<String, TicketTypeLuckDraw> maps = new HashMap<>();
        for (TicketTypeLuckDraw ticketTypeLuckDraw : ticketTypeLuckDrawList) {
            String key = getLuckDrawKey(ticketTypeLuckDraw.getSessionCode(), ticketTypeLuckDraw.getFareBasis());
            maps.put(key, ticketTypeLuckDraw);
        }

        // 根据sessionCode 分组
        Map<String, List<TicketTypeLuckDraw>> mapLists = ticketTypeLuckDrawList.stream().collect(Collectors.groupingBy(TicketTypeLuckDraw::getSessionCode));

        // 抽中列表, 未抽中列表
        List<Long> luckIdList = new ArrayList<>();
        List<Long> missedUserIdList = new ArrayList<>();

        List<TicketTypeLuckDrawUserDTO> repeatDrawUsers = new ArrayList<>();
        //2.进行遍历匹配
        luckDrawUsers.forEach(user -> {
            if (luckIdList.contains(user.getId()) || missedUserIdList.contains(user.getId())) {
                return;
            }
            //3.是否同意降级,是:A-E级遍历匹配,否:按报名时选择的级匹配
            if (0 == user.getAgree()) {
                String key = getLuckDrawKey(user.getSessionCode(), user.getFareBasis());
                TicketTypeLuckDraw ticketTypeLuckDraw = maps.get(key);
                if (ticketTypeLuckDraw.getNumber() <= 0 || ticketTypeLuckDraw.getNumber() < user.getNumber()) {
                    // 未抽中
                    missedUserIdList.add(user.getId());
                    return;
                }

                //场次余票数量减去申请数量
                ticketTypeLuckDraw.setNumber(ticketTypeLuckDraw.getNumber() - user.getNumber());

                //当前用户更新为已抽中状态
                luckIdList.add(user.getId());
            } else {
                //同意,A-E级的场次 A,B,C,D,E 从小到大排序
                List<TicketTypeLuckDraw> ticketTypeLuckDraws = mapLists.get(user.getSessionCode());
                ticketTypeLuckDraws = ticketTypeLuckDraws.stream().sorted(Comparator.comparing(TicketTypeLuckDraw::getFareBasis)).collect(Collectors.toList());

                // 获取当前用户申请的级别位置,防止往上越级抽签
                Integer index = null;
                for (int i = 0; i < ticketTypeLuckDraws.size(); i++) {
                    if (user.getFareBasis().equalsIgnoreCase(ticketTypeLuckDraws.get(i).getFareBasis())) {
                        index = i;
                        break;
                    }
                }
                if (index == null) {
                    missedUserIdList.add(user.getId());
                    return;
                }
                //获取可降级的场次
                List<TicketTypeLuckDraw> demotionSessionsList = ticketTypeLuckDraws.subList(index, ticketTypeLuckDraws.size());
                if (org.springframework.util.CollectionUtils.isEmpty(demotionSessionsList)) {
                    missedUserIdList.add(user.getId());
                    return;
                }
                boolean flag = demotionSessionsList.stream().anyMatch(ticketTypeLuckDraw -> {
                    if (user.getNumber() <= ticketTypeLuckDraw.getNumber()) {
                        // 抽中 终止匹配
                        ticketTypeLuckDraw.setNumber(ticketTypeLuckDraw.getNumber() - user.getNumber());
                        return true;
                    }
                    //继续降级匹配
                    return false;
                });
                if (flag) {
                    //当前用户更新为已抽中状态
                    luckIdList.add(user.getId());
                }else {
                    missedUserIdList.add(user.getId());
                }
            }
        });

//        draw(luckIdList, missedUserIdList, repeatDrawUsers, luckDrawUsers, )

        // 更新抽中列表
        if (luckIdList.size() > 0) {
            typeLuckDrawUserMapper.updateByIds(StringUtils.join(luckIdList, ","), 1);
        }

        // 更新未抽中列表
        if (missedUserIdList.size() > 0) {
            typeLuckDrawUserMapper.updateByIds(StringUtils.join(missedUserIdList, ","), 2);
        }

        // 更新票数量
        if (CollectionUtils.isNotEmpty(ticketTypeLuckDrawList)) {
            updateBatchById(ticketTypeLuckDrawList);
        }
        return Result.success();
    }


    private String getLuckDrawKey(String sessionCode, String fareBasis) {
        return sessionCode + "-" + fareBasis;
    }

    public static void main(String[] args) {
        List<TicketTypeLuckDrawUserDTO> luckDrawUsers = new ArrayList<>();
        TicketTypeLuckDrawUserDTO dto = new TicketTypeLuckDrawUserDTO();
        dto.setUserInfoId(1L);
        luckDrawUsers.add(dto);
        luckDrawUsers.forEach(user-> {
            System.out.println(user);
            luckDrawUsers.addAll(Arrays.asList(dto));
        });
    }

}
